﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Buffers;
using System.IO;
using JetBrains.Annotations;

namespace SnapObjects.Data
{
    internal sealed class SysJsonSequenceSegment : ReadOnlySequenceSegment<byte>
    {
        [CanBeNull] 
        private SysJsonSequenceSegment _next;

        [CanBeNull] 
        private SysJsonSequenceSegment _pre;

        public SysJsonSequenceSegment(int size, [CanBeNull] SysJsonSequenceSegment preSegment = null)
        {
            this.BufferSize = size;
            this.Buffer = ArrayPool<byte>.Shared.Rent(size);
            this.Memory =new ReadOnlyMemory<byte>(this.Buffer);
            
            this.PreSegment = preSegment;
            this.RunningIndex = 0;
        }

        public byte[] Buffer { get; }
        public int BufferSize { get; }
        public int StartIndex { get; set; }
        public int BufferCount { get; private set; }

        [CanBeNull]
        public SysJsonSequenceSegment PreSegment 
        {
            get
            {
                return _pre;
            }
            set
            {
                _pre = value;
                this.RunningIndex = value?.RunningIndex + value?.Memory.Length ?? this.RunningIndex;
            } 
        }

        [CanBeNull]
        public SysJsonSequenceSegment NextSegment
        {
            get
            {
                return _next;
            }
            set
            {
                _next = value;
                this.Next = _next;
            }
        }

        public ReadOnlySpan<byte> Span()
        {
            return this.Memory.Span;
        }

        public void Reset()
        {
            this.BufferCount = 0;
            this.StartIndex = 0;
            this.PreSegment = null;
            this.NextSegment = null;
        }

        public int Load(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException();
            }
            
            this.BufferCount = stream.Read(this.Buffer, 0, this.BufferSize);
            
            this.StartIndex = 0;

            return this.BufferCount;
        }

        public void UpdateStartIndex(long consumedBytes)
        {
            if (consumedBytes == 0)
            {
                return;
            }

            if (consumedBytes >= this.RunningIndex + this.BufferCount)
            {
                this.StartIndex = this.BufferCount;
            }
            else
            {
                this.StartIndex = (int) (consumedBytes - this.RunningIndex);
            }
        }

        public bool Done() => this.StartIndex == this.BufferCount;

        public void SetRunningIndex(long runningIndex) => this.RunningIndex = runningIndex;
    }
}